#include "parser.h"

int parser_parse(string *input, command *cmd)
{
	int ret;
	linked_list *args = ll_new();

	ret = parser_tokenize(input, args, ' ');

	if(ret != INCOMPLETE)
	{
	    parser_search_variables(args);
		ret = parser_process_tokens(args, cmd);
	}

	ll_delete(args, str_delete);
	return ret;
}

void parser_tokenize_increment(ll_iterator *it)
{
    ll_next(it);
}

void parser_tokenize_skip(linked_list *args, string **cur)
{
    if(str_lenght(*cur) != 0) ll_insert_last(args, *cur);
    *cur = str_new();
}

void parser_tokenize_add(string *cur, char c, ll_iterator *it)
{
    str_catc(cur, c);
    parser_tokenize_increment(it);
}

int parser_tokenize(string *input, linked_list *args, char separator)
{
    int state = 1;
    char c;
    string *cur = str_new();
    ll_iterator *it = ll_get_iterator(input);

    while(!ll_is_end(it))
    {
        c = str_get_char(it);

        if(state == 1)
        {
            if(c == separator) parser_tokenize_increment(it);
            else state = 2;
        }
        else if(state == 2)
        {
            if(c == 39) { parser_tokenize_increment(it); state = 4; }
            else if(c == 34) { parser_tokenize_increment(it); state = 5; }
            else if(c == 92) { parser_tokenize_increment(it); state = 3; }
            else if(c == '|') { parser_tokenize_skip(args, &cur); parser_tokenize_add(cur, c, it); state = 7; }
            else if(c == '&') { parser_tokenize_skip(args, &cur); parser_tokenize_add(cur, c, it); state = 6; }
            else if(c == separator) { parser_tokenize_increment(it); parser_tokenize_skip(args, &cur); state = 1; }
            else if(c == '#' || c == ';') { parser_tokenize_skip(args, &cur); parser_tokenize_add(cur, c, it); parser_tokenize_skip(args, &cur); state = 1; }
            else { parser_tokenize_add(cur, c, it); }
        }
        else if(state == 3)
        {
            parser_tokenize_add(cur, c, it); state = 2;
        }
        else if(state == 4)
        {
            if(c == 39) { parser_tokenize_increment(it); state = 2; }
            else parser_tokenize_add(cur, c, it);
        }
        else if(state == 5)
        {
            if(c == 34) { parser_tokenize_increment(it); state = 2; }
            else parser_tokenize_add(cur, c, it);
        }
        else if(state == 6)
        {
            if(c == '&') { parser_tokenize_add(cur, c, it); parser_tokenize_skip(args, &cur); state = 1; }
            else { parser_tokenize_skip(args, &cur); state = 1; }
        }
        else if(state == 7)
        {
            if(c == '|') { parser_tokenize_add(cur, c, it); parser_tokenize_skip(args, &cur); state = 1; }
            else { parser_tokenize_skip(args, &cur); state = 1; }
        }
    }

    parser_tokenize_skip(args, &cur);
    str_delete(cur);
    free(it);

    if(state == 1 || state == 2 || state == 6 || state == 7) return SUCCESS;
    else return FAILURE;
}

void parser_search_variables(linked_list *args)
{
    ll_iterator *it = ll_get_iterator(args);
    string *str;
    string *value;

    while(!ll_is_end(it))
    {
        str = (string *)ll_get(it);
        if(str_starts_with(str, "$"))
        {
            ll_remove_first(str, free);
            value = variable_get(str);
            str_replace(str, value);
            str_delete(value);
        }
        ll_next(it);
    }

    free(it);
}

int parser_process_tokens(linked_list *args, command *cmd)
{
	int ret;
	ll_iterator *i = ll_get_iterator(args);

	ret = parser_process_iterator(i, cmd, STDIN_FILENO);
	free(i);
	return ret;
}

int parser_process_iterator(ll_iterator *i, command *cmd, int in)
{
	string *str;
	int state = 0;
	int connecFlag = 0;
	int next_in = STDIN_FILENO;

	if(ll_is_end(i)) return SUCCESS;

	str = (string *) ll_get(i);

	if(str_starts_with(str, "/") || str_starts_with(str, "./") || str_starts_with(str, "../"))
		cmd->type = LOCAL;
	else if(is_internal_cmd(str))
		cmd->type = INTERNAL;
	else
		cmd->type = EXTERNAL;

	ll_insert_last(cmd->args, str_cpy(str));

	ll_next(i);

	cmd->ground = FG;
	cmd->in_fd = in;

	while(!ll_is_end(i) && !connecFlag)
	{
		str = (string *) ll_get(i);

		if(state == SIMPLE_IN)
		{
		    if(cmd->in_fd != STDIN_FILENO) close(cmd->in_fd);
		    char *file = str_to_array(str);
			cmd->in_fd = open(file, O_RDONLY);
			if(cmd->in_fd == -1)
			{
			    fprintf(stderr, "%s: No such file or directory\n", file);
			    free(file);
			    return EXIT;
			}
			state = 0;
			free(file);
		}
		else if(state == SIMPLE_OUT || state == DOUBLE_OUT)
		{
		    if(cmd->out_fd != STDOUT_FILENO) close(cmd->out_fd);
		    char *file = str_to_array(str);
		    if(state == SIMPLE_OUT)
		        cmd->out_fd = open(file, O_WRONLY | O_CREAT | O_TRUNC, 0664);
		    else
		        cmd->out_fd = open(file, O_WRONLY | O_CREAT | O_APPEND, 0664);
			free(file);
			state = 0;
		}
		else if(state == SIMPLE_ERR || state == DOUBLE_ERR)
		{
		    if(cmd->err_fd != STDERR_FILENO) close(cmd->err_fd);
		    char *file = str_to_array(str);
		    if(state == SIMPLE_ERR)
		        cmd->err_fd = open(file, O_WRONLY | O_CREAT | O_TRUNC, 0664);
		    else
		        cmd->err_fd = open(file, O_WRONLY | O_CREAT | O_APPEND, 0664);
            free(file);
            state = 0;
		}

		else
		{
		    state = is_redirect_char(str);

		    if(state == OUT_ERR)
		    {
		        if(cmd->out_fd != STDOUT_FILENO) close(cmd->out_fd);
		        cmd->out_fd = cmd->err_fd;
		        state = 0;
		    }

		    else if(state == ERR_OUT)
            {
                if(cmd->err_fd != STDERR_FILENO) close(cmd->err_fd);
                cmd->err_fd = cmd->out_fd;
                state = 0;
            }

		    else if(state == 0)
			{
				connecFlag = is_connector(str);
				if(connecFlag)
				{
					cmd->ground = (connecFlag == EBG) ? BG : FG;
					if(connecFlag == PIPE)
					{
					    int fd[2];
					    if(pipe(fd) == -1) perror("Couldn't create a pipe\n");
					    else if(cmd->out_fd == STDOUT_FILENO)
					    {
					        cmd->out_fd = fd[1];
					        next_in = fd[0];
					    }
					}
					cmd->connec = connector_new();
					cmd->connec->type = connecFlag;
				}
				else
					ll_insert_last(cmd->args, str_cpy(str));
			}
		}

		ll_next(i);
	}

	if(state != 0) return FAILURE;

	if(connecFlag)
	{
	    if(!ll_is_end(i))
	    {
	        cmd->connec->next = command_new();
            return parser_process_iterator(i, cmd->connec->next, next_in);
	    }
	    else if(connecFlag != EBG)
	        return INCOMPLETE;
	}

	return SUCCESS;
}


